package larrys.pomodoro.daoimpl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import larrys.pomodoro.daoimpl.rowmappers.ActivityRowMapper;
import larrys.pomodoro.domain.entity.Activity;
import larrys.pomodoro.domain.repository.ActivityRepository;
import larrys.pomodoro.util.AssertUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import static java.sql.Types.*;

@Repository
public class ActivityRepositoryImpl implements ActivityRepository {

	private static Log logger = LogFactory.getLog(ActivityRepositoryImpl.class);
	private JdbcTemplate jdbcTemplate;

	@Autowired
	@Required
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	private final static String SQL_QUERY_AVAILABLE_ACTIVITIES_BY_USERID = "SELECT * FROM activity WHERE deleted = 0 AND user_id = ? AND finished = ?";

	@Override
	public Collection<Activity> getActivitiesByUserId(long userId,
			boolean finished) {
		AssertUtil.validId(userId);
		logger.debug("Trying to get available activities by user[id:" + userId
				+ "]");
		List<Activity> activities = new ArrayList<Activity>();
		try {
			activities = jdbcTemplate.query(
					SQL_QUERY_AVAILABLE_ACTIVITIES_BY_USERID, new Object[] {
							userId, finished }, new int[] { BIGINT, TINYINT },
					new ActivityRowMapper());
		} catch (RuntimeException e) {
			logger.error(
					"Exception threw while getting available activities by user[id:"
							+ userId + "]", e);
			throw e;
		}
		return activities;
	}

	private final static String SQL_QUERY_ACTIVITY_BY_ID = "SELECT * FROM activity WHERE deleted = 0 AND id = ?";

	@Override
	public Activity getActivityById(long activityId) {
		AssertUtil.validId(activityId);
		logger.debug("Trying to get Activity by Activity[id:" + activityId
				+ "]");
		Activity activity = jdbcTemplate.queryForObject(
				SQL_QUERY_ACTIVITY_BY_ID, new Object[] { activityId },
				new int[] { BIGINT }, new ActivityRowMapper());
		logger.debug("Returning " + activity);
		return activity;
	}

	private final static String SQL_INSERT_ACTIVITY = "INSERT INTO activity (id, name, estimation, description, comment, finished, permanent, deleted, user_id, plan_date, priority) VALUES(null,?,?,?,?,?,?,0,?,?,?)";

	@Override
	public void addActivity(Activity activity) {
		AssertUtil.argumentNotNull(activity, "activity");
		AssertUtil.propertyShouldNotBeNull(activity.getName(), "activity",
				"name");
		logger.debug("Trying to add new " + activity);
		try {
			jdbcTemplate.update(SQL_INSERT_ACTIVITY,
					new Object[] { activity.getName(),
							activity.getEstimation(),
							activity.getDescription(), activity.getComment(),
							activity.isFinished(), activity.isPermanent(),
							activity.getUserId(), activity.getPlanDate(),
							activity.getPriority() }, new int[] { VARCHAR,
							INTEGER, VARCHAR, VARCHAR, TINYINT, TINYINT,
							BIGINT, TIMESTAMP, INTEGER });
		} catch (RuntimeException e) {
			logger.error("Exception threw while adding " + activity, e);
			throw e;
		}
		logger.info(activity + " added successfully!");
	}

	private final static String SQL_UPDATE_ACTIVITY = "UPDATE activity SET name = ?, user_id = ?, description = ?, comment = ?, estimation = ?, permanent = ?, finished = ?, priority = ? WHERE id = ?";

	@Override
	public void updateActivity(Activity activity) {
		AssertUtil.argumentNotNull(activity, "activity");
		AssertUtil.validId(activity.getId());
		AssertUtil.propertyShouldNotBeNull(activity.getName(), "activity",
				"name");
		logger.debug("Trying to update " + activity);
		try {
			jdbcTemplate.update(SQL_UPDATE_ACTIVITY,
					new Object[] { activity.getName(), activity.getUserId(),
							activity.getDescription(), activity.getComment(),
							activity.getEstimation(), activity.isPermanent(),
							activity.isFinished(), activity.getPriority(),
							activity.getId() }, new int[] { VARCHAR, BIGINT,
							VARCHAR, VARCHAR, INTEGER, TINYINT, TINYINT,
							INTEGER, BIGINT });
		} catch (RuntimeException e) {
			logger.error("Exception threw while updating " + activity, e);
			throw e;
		}
		logger.info(activity + " updated successfully!");
	}

	private final static String SQL_REAL_DELETE_ACTIVITY = "DELETE FROM activity WHERE id = ?";

	@Override
	public void realDeleteActivity(long activityId) {
		try {
			jdbcTemplate.update(SQL_REAL_DELETE_ACTIVITY,
					new Object[] { activityId }, new int[] { BIGINT });
		} catch (RuntimeException e) {
			logger.error("Exception threw while real delate Activity[id: "
					+ activityId + "]", e);
			throw e;
		}
	}

	private final static String SQL_UPDATE_MARK_ACTIVITY_DELETED = "UPDATE activity SET deleted = 1 WHERE id = ?";

	@Override
	public void markActivityDeleted(long activityId) {
		AssertUtil.validId(activityId);
		logger.debug("Trying to mark deleted Activity[id:" + activityId + "]");
		try {
			jdbcTemplate.update(SQL_UPDATE_MARK_ACTIVITY_DELETED,
					new Object[] { activityId }, new int[] { BIGINT });
		} catch (RuntimeException e) {
			logger.error("Exception threw while mark deleted Activity[id:"
					+ activityId + "]", e);
			throw e;
		}
		logger.info("Activity[id:" + activityId
				+ "] marked deleted successfully!");
	}

	private final static String SQL_UPDATE_MARK_ACTIVITY_FINISHED = "UPDATE activity SET finished = 1 WHERE id = ?";

	@Override
	public void markActivityFinished(long activityId) {
		AssertUtil.validId(activityId);
		logger.debug("Trying to mark finished Activity[id:" + activityId + "]");
		try {
			jdbcTemplate.update(SQL_UPDATE_MARK_ACTIVITY_FINISHED,
					new Object[] { activityId }, new int[] { BIGINT });
		} catch (RuntimeException e) {
			logger.error("Exception threw while marking deleted Activity[id:"
					+ activityId + "]", e);
			throw e;
		}
		logger.info("Activity[id:" + activityId
				+ "] marked finished successfully!");
	}

}
